#include <tinx/task.h>
#include <tinx/memory.h>
#include <tinx/fs.h>
#include <tinx/fifo.h>
#include <tinx/arena.h>

static inode_t *get_pipe_inode()
{
    inode_t *inode = get_free_inode();
    inode->desc = kmalloc(sizeof(fifo_t));
    inode->buf = (buffer_t *)alloc_kpage(1);
    inode->count = 2;
    inode->op = fs_get_op(FS_TYPE_PIPE);
    fifo_init((fifo_t *)inode->desc, (char *)inode->buf, PAGE_SIZE);
    return inode;
}

int pipe_close(inode_t *inode)
{
    // 清除 fifo 结构
    fifo_t *fifo = (fifo_t *)inode->desc;
    kfree(fifo);
    // 删除缓冲区
    free_kpage((u32)inode->buf, 1);
}

int pipe_read(inode_t *inode, void *addr, int count, int offset)
{
    char *buf = addr;
    task_t *task = running_task();
    fifo_t *fifo = (fifo_t *)inode->desc;
    int nr = 0;
    while (nr < count)
    {
        if (fifo_empty(fifo))
        {
            inode->rxwaiter = task;
            task_block(task, TASK_BLOCKED);
        }
        buf[nr++] = fifo_get(fifo);
        if (inode->wxwaiter)
        {
            task_unblock(inode->wxwaiter, EOK);
            inode->wxwaiter = NULL;
        }
    }
    return nr;
}

// 写管道
int pipe_write(inode_t *inode, void *addr, int count, int offset)
{
    char *buf = addr;
    task_t *task = running_task();
    fifo_t *fifo = (fifo_t *)inode->desc;
    int nr = 0;
    while (nr < count)
    {
        if (fifo_full(fifo))
        {
            inode->wxwaiter = task;
            task_block(task, TASK_BLOCKED);
        }
        fifo_put(fifo, buf[nr++]);
        if (inode->rxwaiter)
        {
            task_unblock(inode->rxwaiter, EOK);
            inode->rxwaiter = NULL;
        }
    }
    return nr;
}

// 创建管道
int sys_pipe(fd_t pipefd[2])
{
    inode_t *inode = get_pipe_inode();

    task_t *task = running_task();

    file_t *files[2];

    pipefd[0] = task_get_file(task);
    files[0] = task->files[pipefd[0]] = get_file();

    pipefd[1] = task_get_file(task);
    files[1] = task->files[pipefd[1]] = get_file();

    file_t *file = files[0];
    file->flags = O_RDONLY;
    file->inode = inode;

    file++;
    file->flags = O_WRONLY;
    file->inode = inode;

    return EOK;
}

static fs_op_t pipe_op = {
    fs_default_nosys,

    fs_default_nosys,
    fs_default_nosys,

    fs_default_nosys,
    pipe_close,

    pipe_read,
    pipe_write,

    fs_default_nosys,

    fs_default_nosys,
    fs_default_nosys,

    fs_default_nosys,
    fs_default_nosys,
    fs_default_nosys,

    fs_default_nosys,
    fs_default_nosys,
    fs_default_nosys,
    fs_default_nosys,
    fs_default_nosys,
    fs_default_nosys
};

void pipe_init()
{
    fs_register_op(FS_TYPE_PIPE, &pipe_op);
}